﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using Flagwind.Data;

namespace Flagwind.Utilities.DataBuilder
{
	public partial class FDbConnectionWizard : Form
	{
		#region 私有字段

		private int _originalHeight;
		private int _shortHeight;
		private List<KeyValueItem> _dataEngines;
		private DataProviderType _providerType;

		#endregion

		#region 私有属性

		/// <summary>
		/// 获取数据访问提供程序引擎。
		/// </summary>
		private List<KeyValueItem> DataEngines
		{
			get
			{
				if(_dataEngines == null)
				{
					_dataEngines = new List<KeyValueItem>();

					_dataEngines.Add(new KeyValueItem("SQLServer数据库服务", "SqlServer"));
					_dataEngines.Add(new KeyValueItem("SQLServer数据库文件", "SqlServer_File"));
					_dataEngines.Add(new KeyValueItem("SQLServer CE", "SqlServerCE"));
					_dataEngines.Add(new KeyValueItem("Oracle数据库服务", "Oracle"));
					_dataEngines.Add(new KeyValueItem("Access", "Access"));
					_dataEngines.Add(new KeyValueItem("其它数据库驱动程序", "Other"));
				}

				return _dataEngines;
			}
		}

		/// <summary>
		/// 获取当前选择的数据引擎(小写)。
		/// </summary>
		private string DataEngine
		{
			get
			{
				var selectedItem = cmbDataEngine.SelectedItem as KeyValueItem;

				if(selectedItem != null)
					return selectedItem.Value.ToLower();

				return string.Empty;
			}
		}

		/// <summary>
		/// 获取当前的登录方式。
		/// </summary>
		private string LoginType
		{
			get
			{
				var selectedItem = cmbLoginType.SelectedItem as KeyValueItem;

				if(selectedItem != null)
					return selectedItem.Value.ToLower();

				return string.Empty;
			}
		}

		#endregion

		#region 公共属性

		/// <summary>
		/// 获取或设置连接字符串。
		/// </summary>
		public string ConnectionString
		{
			get
			{
				return this.txtConnectionString.Text;
			}
			set
			{
				this.txtConnectionString.Text = value;
			}
		}

		/// <summary>
		/// 获取数据访问提供程序完整信息。
		/// </summary>
		public string Provider
		{
			get
			{
				if(this._providerType != DataProviderType.UNKNOWN)
				{
					return this.txtDataProviderType.Text;
				}

				return string.Empty;
			}
		}

		/// <summary>
		/// 获取服务器名称。
		/// </summary>
		public string ServerName
		{
			get
			{
				return this.txtServerName.Text;
			}
		}

		/// <summary>
		/// 获取数据访问提供程序类型。
		/// </summary>
		public DataProviderType ProviderType
		{
			get
			{
				return _providerType;
			}
		}

		#endregion

		#region 构造方法

		/// <summary>
		/// 初始化 FDbConnectionWizard 类的新实例。
		/// </summary>
		public FDbConnectionWizard()
		{
			InitializeComponent();
		}

		#endregion

		#region 事件方法

		/// <summary>
		/// 窗体加载完成时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void FDbConnectionWizard_Load(object sender, EventArgs e)
		{
			this._originalHeight = this.Height;
			this._shortHeight = btnSubmit.Location.Y + btnSubmit.Height + 60;

			this.Height = this._shortHeight;

			this.cmbDataEngine.DataSource = new BindingSource(){DataSource = this.DataEngines};
			this.cmbDataEngine.DisplayMember = "Key";
			this.cmbDataEngine.ValueMember = "Value";
		}

		/// <summary>
		/// 数据引擎选择发生更改时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void cmbDataEngine_SelectedIndexChanged(object sender, EventArgs e)
		{
			this.txtUserName.Enabled = true;
			this.txtPassword.Enabled = true;
			this.btnProviderBrowser.Enabled = false;

			var mssqlLoginType = new List<KeyValueItem>();
			mssqlLoginType.Add(new KeyValueItem("Windows集成验证", "window"));
			mssqlLoginType.Add(new KeyValueItem("SQLServer 身份验证", "user"));

			switch(this.DataEngine)
			{
				case "sqlserver":
				{
					this.lblServerName.Text = "服务器地址：";
					this.lblLoginType.Text = "登录方式：";

					this.cmbLoginType.Enabled = true;
					this.cmbLoginType.DataSource = new BindingSource(){DataSource = mssqlLoginType};
					this.cmbLoginType.DisplayMember = "Key";
					this.cmbDataEngine.ValueMember = "Value";

					this.btnFileBrowser.Visible = false;
					this.txtServerName.Text = "";
					this._providerType = DataProviderType.SqlServer;
					this.txtDataProviderType.Text = "Flagwind.Data.Providers.SqlServer, Flagwind.Data";

					break;
				}
				case "sqlserver_file":
				{
					this.lblServerName.Text = "文件地址：";
					this.lblLoginType.Text = "登录方式：";

					this.btnFileBrowser.Visible = true;

					this.cmbLoginType.DataSource = new BindingSource(){DataSource = mssqlLoginType};
					this.cmbLoginType.DisplayMember = "Key";
					this.cmbDataEngine.ValueMember = "Value";
					this.cmbLoginType.Enabled = true;
					this.txtServerName.Text = "";
					this.openFileDialog.Filter = "数据库文件*.mdf|*.mdf|所有文件|*.*";
					this._providerType = DataProviderType.SqlServer;
					this.txtDataProviderType.Text = "Flagwind.Data.Providers.SqlServer, Flagwind.Data";

					break;
				}
				case "sqlserverce":
				{
					var loginTypes = new List<KeyValueItem>();
					loginTypes.Add(new KeyValueItem("Windows集成验证", "window"));

					this.lblServerName.Text = "文件地址：";
					this.lblLoginType.Text = "登录方式：";
					this.btnFileBrowser.Visible = true;
					this.cmbLoginType.DataSource = new BindingSource(){DataSource = loginTypes};
					this.cmbLoginType.DisplayMember = "Key";
					this.cmbDataEngine.ValueMember = "Value";
					this.cmbLoginType.Enabled = true;
					this.txtPassword.Enabled = true;
					this.txtServerName.Text = "";
					this.openFileDialog.Filter = "数据库文件*.sf|*sdf|所有文件|*.*";
					this._providerType = DataProviderType.SqlServerCe;
					this.txtDataProviderType.Text = "Flagwind.Data.Providers.SqlServerCe, Flagwind.Data.SqlServerCe";

					break;
				}
				case "access":
				{
					var loginTypes = new List<KeyValueItem>();
					loginTypes.Add(new KeyValueItem("Access(2000-2003)集成验证", "2000-2003"));
					loginTypes.Add(new KeyValueItem("Access(2007)", "2007"));

					this.lblServerName.Text = "文件地址：";
					this.btnFileBrowser.Visible = true;
					this.lblLoginType.Text = "版本：";
					this.cmbLoginType.DataSource = new BindingSource(){DataSource = loginTypes};
					this.cmbLoginType.DisplayMember = "Key";
					this.cmbDataEngine.ValueMember = "Value";
					this.cmbLoginType.Enabled = true;
					this.txtServerName.Text = "";
					this.openFileDialog.Filter = "Access2000-2003|*.mdb|Access2007|*.accdb|所有文件|*.*";
					this._providerType = DataProviderType.Access;
					this.txtDataProviderType.Text = "Flagwind.Data.Providers.Access, Flagwind.Data";

					break;
				}
				case "oracle":
				{
					var loginTypes = new List<KeyValueItem>();
					loginTypes.Add(new KeyValueItem("Normal", "normal"));
					loginTypes.Add(new KeyValueItem("SysDBA", "sysdba"));

					this.lblServerName.Text = "服务名：";
					this.lblLoginType.Text = "登录类型：";
					this.cmbLoginType.Enabled = true;
					this.cmbLoginType.DataSource = new BindingSource(){DataSource = loginTypes};
					this.cmbLoginType.DisplayMember = "Key";
					this.cmbDataEngine.ValueMember = "Value";
					this.btnFileBrowser.Visible = false;
					this.txtServerName.Text = "";
					this._providerType = DataProviderType.Oracle;
					this.txtDataProviderType.Text = "Flagwind.Data.Providers.Oracle, Flagwind.Data.Oracle";

					break;
				}
				default:
				{
					this.btnFileBrowser.Visible = true;
					this.txtServerName.Text = "";
					this.openFileDialog.Filter = "数据访问提供程序|*.dll";
					this.cmbLoginType.DataSource = null;
					this.cmbLoginType.Enabled = false;
					this.txtUserName.Enabled = true;
					this.txtPassword.Enabled = true;
					this.txtDataProviderType.Text = "";
					this.btnFileBrowser.Enabled = false;
					this.btnProviderBrowser.Enabled = true;

					break;
				}
			}
		}

		/// <summary>
		/// 当高级选项复选框发生改变时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void chkAdvanced_CheckedChanged(object sender, EventArgs e)
		{
			this.Height = chkAdvanced.Checked ? this._originalHeight : this._shortHeight;

			if(this.chkAdvanced.Checked)
			{
				this.txtConnectionString.Text = this.GenerateConnectionString();
			}
		}

		/// <summary>
		/// 当数据提供程序按钮点击时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnProviderBrowser_Click(object sender, EventArgs e)
		{
			this.openFileDialog.InitialDirectory = Application.StartupPath;
			this.openFileDialog.FileName = string.Empty;
			this.openFileDialog.ShowDialog();

			if(this.DataEngine.Equals("other") && !string.IsNullOrEmpty(this.openFileDialog.FileName))
			{
				bool find = false;
				this.txtServerName.Text = string.Empty;
				var assembly = Assembly.LoadFrom(this.openFileDialog.FileName);

				foreach(Type type in assembly.GetTypes())
				{
					var baseType = type.BaseType;

					if(baseType != null && baseType.FullName != null && baseType.FullName.Equals("Flagwind.Data.DataProviderBase"))
					{
						string fullName = type.FullName;

						if(baseType.AssemblyQualifiedName == typeof(DataProviderBase).AssemblyQualifiedName)
						{
							find = true;

							this.txtDataProviderType.Text = fullName + ", " + Path.GetFileNameWithoutExtension(this.openFileDialog.FileName);

							if(Path.GetDirectoryName(this.openFileDialog.FileName) == Application.StartupPath)
							{
								var dataProvider = (IDataProvider)Activator.CreateInstance(type);

								this._providerType = dataProvider.ProviderType;

								this.UpdateForm(this._providerType);

							}
							else
							{
								MessageBox.Show("请将选定的程序集相关的其它附属程序集复制到当前程序的运行目录：" + Application.StartupPath);
							}

							break;
						}
						else
						{
							MessageBox.Show("选定的程序集中实现 DataProviderBase 的子类 [" + fullName + "] 所在的程序集与当前程序集(Flagwind.Data.dll)不匹配，请重新编译你选定的程序集。\r\n" + baseType.AssemblyQualifiedName + "\r\n" + type.AssemblyQualifiedName);

							return;
						}
					}
				}

				if(!find)
				{
					MessageBox.Show("在选定的程序集中未找到实现[Flagwind.Data.DataProviderBase。", "数据提供程序", MessageBoxButtons.OK, MessageBoxIcon.Hand);
				}
			}
		}

		/// <summary>
		/// 当文件选择按钮点击时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnFileBrowser_Click(object sender, EventArgs e)
		{
			this.openFileDialog.FileName = string.Empty;
			this.openFileDialog.ShowDialog();

			if(!this.DataEngine.Equals("other") && !string.IsNullOrEmpty(this.openFileDialog.FileName))
			{
				txtServerName.Text = this.openFileDialog.FileName;
			}
		}

		/// <summary>
		/// 当登录方式发生改变时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void cmbLoginType_SelectedIndexChanged(object sender, EventArgs e)
		{
			if(this.LoginType.Equals("window"))
			{
				this.txtUserName.Enabled = false;
				this.txtPassword.Enabled = false;
			}
			else
			{
				this.txtUserName.Enabled = true;
				this.txtPassword.Enabled = true;
			}
		}

		/// <summary>
		/// 当点击刷新连接时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void lnkRefesh_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			txtConnectionString.Text = this.GenerateConnectionString();
		}

		/// <summary>
		/// 当点击确定按钮时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnSubmit_Click(object sender, EventArgs e)
		{
			if(string.IsNullOrWhiteSpace(txtServerName.Text))
			{
				MessageBox.Show(this.lblServerName.Text.Replace("：", string.Empty) + "不能为空!");
			}
			else
			{
				// 重新生成数据库连接字符串。
				this.ConnectionString = this.GenerateConnectionString();

				this.Close();
			}
		}

		/// <summary>
		/// 当点击取消按钮时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnCancel_Click(object sender, EventArgs e)
		{
			this.ConnectionString = string.Empty;

			this.Close();
		}

		/// <summary>
		/// 当点击保存连接时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnSaveConnection_Click(object sender, EventArgs e)
		{

		}

		/// <summary>
		/// 当点击测试连接时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnTestConnection_Click(object sender, EventArgs e)
		{
			this.ConnectionString = this.GenerateConnectionString();

			if(!string.IsNullOrEmpty(this.ConnectionString))
			{
				try
				{
					var connection = DataProviderFactory.GetProvider(this.Provider, this.ConnectionString).GetConnection();
					connection.Open();
					connection.Close();
					MessageBox.Show("测试成功！", "测试连接", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
				}
				catch(Exception ex)
				{
					MessageBox.Show(ex.Message, "测试连接", MessageBoxButtons.OK, MessageBoxIcon.Hand);
				}

			}
		}

		#endregion

		#region 私有方法

		/// <summary>
		/// 根据指定的数据访问提供程序改变UI。
		/// </summary>
		/// <param name="providerType">数据访问提供程序类型。</param>
		private void UpdateForm(DataProviderType providerType)
		{
			switch(providerType)
			{
				case DataProviderType.MySql:
				case DataProviderType.PostgreSQL:
				{
					this.lblServerName.Text = "服务器地址：";
					this.cmbLoginType.Enabled = false;
					this.btnFileBrowser.Visible = false;
					this.txtServerName.Text = string.Empty;

					break;
				}
				case DataProviderType.SQLite:
				{
					this.lblServerName.Text = "文件地址：";
					this.btnFileBrowser.Visible = true;
					this.cmbLoginType.Enabled = false;
					this.btnFileBrowser.Enabled = true;
					this.openFileDialog.Filter = "SQLite数据库文件|*.*";

					break;
				}
				default:
				{
					this.btnFileBrowser.Visible = true;
					this.txtServerName.Text = string.Empty;
					this.openFileDialog.Filter = "数据访问提供程序|*.dll";
					this.cmbLoginType.DataSource = null;
					this.cmbLoginType.Enabled = false;
					this.txtUserName.Enabled = true;
					this.txtPassword.Enabled = true;
					this.txtDataProviderType.Text = string.Empty;
					this.btnFileBrowser.Enabled = false;
					this.btnProviderBrowser.Enabled = true;

					break;
				}
			}
		}

		/// <summary>
		/// 生成数据访问连接字符串。
		/// </summary>
		/// <returns>数据访问连接字符串。</returns>
		private string GenerateConnectionString()
		{
			string connectionString = string.Empty;

			switch(this.DataEngine)
			{
				case "sqlserver":
				{
					if(this.LoginType.Equals("window"))
					{
						connectionString = string.Format("Data Source={0};Integrated Security=True;", txtServerName.Text);
					}
					else
					{
						connectionString = string.Format("Data Source={0};User Id={1};Password={2};", txtServerName.Text, txtUserName.Text, txtPassword.Text);
					}

					break;
				}
				case "sqlserver_file":
				{
					connectionString = string.Format(@"Data Source=.\SQLEXPRESS;AttachDbFilename={0};Connect Timeout=30;User Instance=True;", txtServerName.Text);

					if(this.LoginType.ToString().Equals("window"))
					{
						connectionString = connectionString + "Integrated Security=True;";
					}
					else
					{
						connectionString = connectionString + string.Format("User Id={0};Password={1};", txtUserName.Text, txtPassword.Text);
					}

					break;
				}
				case "sqlserverce":
				{
					connectionString = string.Format("Data Source={0};Persist Security Info=True;", txtServerName.Text);

					if(txtPassword.Enabled && !string.IsNullOrWhiteSpace(txtPassword.Text))
					{
						connectionString = connectionString + string.Format("Password={0};", txtPassword.Text);
					}

					break;
				}
				case "access":
				{
					if(this.LoginType.Equals("2000-2003"))
					{
						connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};", txtServerName.Text);
					}
					else
					{
						connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};", txtServerName.Text);
					}

					break;
				}
				case "oracle":
				{
					connectionString = string.Format("Data Source={0};User Id={1};Password={2};Integrated Security=no;", txtServerName.Text, txtUserName.Text, txtPassword.Text);

					break;
				}
				case "other":
				{
					if(this.ProviderType == DataProviderType.MySql || this.ProviderType == DataProviderType.PostgreSQL)
					{
						connectionString = string.Format("server={0};User Id={1};password={2};", txtServerName.Text, txtUserName.Text, txtPassword.Text);
					}
					else if(this.ProviderType == DataProviderType.SQLite)
					{
						connectionString = string.Format("Data Source={0};", txtServerName.Text);
					}
					else
					{
						connectionString = txtConnectionString.Text;
					}

					break;
				}
			}

			return connectionString;
		}

		#endregion
	}
}
